package com.zone.lib.download.install;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.alibaba.fastjson.JSON;
import com.zone.lib.download.db.entity.Download;
import com.zone.lib.download.enums.AppStatusEnum;
import com.zone.lib.download.utils.AppStatusUpdateNotify;
import com.zone.lib.download.utils.IoUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class AndroidP {
    private static final String TAG = AndroidP.class.getName();

    private static final int INSTALL_COPY_SUCCESS = 0;
    private static final int INSTALL_FAIL_FILE_NOT_FOUND = 1;
    private static final int INSTALL_FAIL_COPY_ERROR = 2;
    private static final int INSTALL_FAIL_IO_ERROR = 3;

    public static final String ACTION_INSTALL = "ACTION_INSTALL";
    public static final String ACTION_UNINSTALL = "ACTION_UNINSTALL";

    // 适配android9的安装方法。
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public static void install28(Context context, Download mDownload) {
        Log.d(TAG, "install start info=" + JSON.toJSONString(mDownload));
        mDownload.setDownloadStatus(AppStatusEnum.INSTALL_PREPARE.CODE);
        AppStatusUpdateNotify.appNotify(context, mDownload);
        File apkFile = new File(mDownload.getFilePath());
        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        sessionParams.setSize(apkFile.length());

        int sessionId = createSession(packageInstaller, sessionParams);
        Log.d(TAG, "install sessionId=" + sessionId);
        if (sessionId != -1) {
            int copyRet = copyInstallFile(packageInstaller, sessionId, mDownload.getFilePath());
            Log.d(TAG, "install copySuccess=" + copyRet);
            if (copyRet == INSTALL_FAIL_FILE_NOT_FOUND) {
                mDownload.setDownloadStatus(AppStatusEnum.DOWNLOAD_DEFAULT.CODE);
                AppStatusUpdateNotify.appNotify(context, mDownload);
            } else if (copyRet == INSTALL_FAIL_IO_ERROR) {
                mDownload.setDownloadStatus(AppStatusEnum.INSTALL_FAIL.CODE);
                AppStatusUpdateNotify.appNotify(context, mDownload);
            } else {
                execInstallCommand(context, packageInstaller, sessionId, mDownload);
            }
        } else {
            Log.d(TAG, "create session fail ");
            mDownload.setDownloadStatus(AppStatusEnum.INSTALL_FAIL.CODE);
            AppStatusUpdateNotify.appNotify(context, mDownload);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static int createSession(PackageInstaller packageInstaller,
                                     PackageInstaller.SessionParams sessionParams) {
        int sessionId = -1;
        try {
            sessionId = packageInstaller.createSession(sessionParams);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionId;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static int copyInstallFile(PackageInstaller packageInstaller,
                                           int sessionId, String apkFilePath) {
        InputStream in = null;
        OutputStream out = null;
        PackageInstaller.Session session = null;
        try {
            File apkFile = new File(apkFilePath);
            if (!apkFile.exists()) {
                Log.e(TAG, "error install file of not found");
                return INSTALL_FAIL_FILE_NOT_FOUND;
            }
            session = packageInstaller.openSession(sessionId);
            out = session.openWrite("base.apk", 0, apkFile.length());
            in = new FileInputStream(apkFile);
            int total = 0, c;
            byte[] buffer = new byte[65536];
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
            Log.e(TAG, "streamed " + total + " bytes");
        } catch (IOException e) {
            e.printStackTrace();
            return INSTALL_FAIL_IO_ERROR;
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(session);
        }
        return INSTALL_COPY_SUCCESS;
    }

    private static void execInstallCommand(Context context, PackageInstaller packageInstaller, int sessionId, Download mDownload) {
//        Intent intent = new Intent(context, InstallResultReceiver.class);
        Intent intent = new Intent();
//        intent.putExtra("download", mDownload);
        intent.setAction(ACTION_INSTALL);
        intent.putExtra("code", mDownload.getCode());
        mDownload.setDownloadStatus(AppStatusEnum.INSTALL_LOADING.CODE);
        AppStatusUpdateNotify.appNotify(context, mDownload);
        execInstallCommand(context, packageInstaller, sessionId, intent);
    }

    private static void execInstallCommand(Context context, PackageInstaller packageInstaller, int sessionId, Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            PackageInstaller.Session session = null;
            try {
                session = packageInstaller.openSession(sessionId);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                session.commit(pendingIntent.getIntentSender());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IoUtils.closeQuietly(session);
            }
        }
    }

    /**
     * 根据包名卸载应用
     *
     * @param packageName
     */
    public static boolean uninstall(Context context, String code, String packageName) {
//        Intent broadcastIntent = new Intent(context, InstallResultReceiver.class);
        Intent intent = new Intent();
        intent.setAction(ACTION_UNINSTALL);
        intent.putExtra("code", code);
        intent.putExtra("packageName", packageName);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 2,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        PackageInstaller packageInstaller = null;
        try {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                packageInstaller = context.getPackageManager().getPackageInstaller();
                packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
            }
            return true;
        } catch (IllegalArgumentException e) {
            Log.e(TAG, e.toString());
            return false;
        }
    }

    public static void startApp(Context context, String packageName) {
        Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
        if (intent != null) {
            intent.setPackage(null);
            context.startActivity(intent);
        }
    }

}
